package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.util.IPUtils;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Aspect 注解描述的类型为切面对象类型,此切面中可以定义多个切入点和通知方法.
 */
@Order
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    /**
     * @Pointcut注解用于定义切入点
     * bean("spring容器中bean的名字")这个表达式为切入点表达式定义的一种语法,
     * 它描述的是某个bean或多个bean中所有方法的集合为切入点,这个形式的切入点
     * 表达式的缺陷是不能精确到具体方法的.
     */
    //@Pointcut("bean(sysUserServiceImpl)")
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}//此方法只负责承载切入点的定义

    /**
     * @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,
     * 在当前业务中,此方法为日志通知方法
     * @param jp 连接点对象,此对象封装了要执行的切入点方法信息.
     * 可以通过连接点对象调用目标方法.
     * @return 目标方法的执行结果
     * @throws Throwable
     */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp)throws Throwable{
        long t1=System.currentTimeMillis();
        log.info("Start:{}",System.currentTimeMillis());
        try {
            Object result = jp.proceed();//执行目标方法(切点方法中的某个方法)
            long t2=System.currentTimeMillis();
            log.info("After:{}",System.currentTimeMillis());
            saveUserLog(jp,t2-t1);
            return result;//目标业务方法的执行结果
        }catch(Throwable e){
            e.printStackTrace();
            log.error("Exception:{}",System.currentTimeMillis());
            throw e;
        }
    }
    @Autowired
    private SysLogService sysLogService;

    private void saveUserLog(ProceedingJoinPoint jp,long time) throws JsonProcessingException, NoSuchMethodException {
        String username="cgb";
        String ip= IPUtils.getIpAddr();
        Class<?> targetClass=jp.getTarget().getClass();
        MethodSignature ms=(MethodSignature) jp.getSignature();
        Method targetMethod=targetClass.getMethod(ms.getName(),ms.getParameterTypes());
        RequiredLog annotation=targetMethod.getAnnotation(RequiredLog.class);
        String operation=annotation.value();
        String classMethodName="";
        Object[] args = jp.getArgs();
        String params=new ObjectMapper().writeValueAsString(args);
        SysLog sysLog=new SysLog();
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setOperation(operation);
        sysLog.setMethod(classMethodName);
        sysLog.setParams(params);
        sysLog.setTime(time);

        sysLogService.saveObject(sysLog);
    }
}
